package com.yubest;

/**
 * 一条包含字母 A-Z 的消息通过以下映射进行了 编码 ：
 *
 * 'A' -> 1
 * 'B' -> 2
 * ...
 * 'Z' -> 26
 * 要 解码 已编码的消息，所有数字必须基于上述映射的方法，反向映射回字母（可能有多种方法）。例如，"11106" 可以映射为：
 *
 * "AAJF" ，将消息分组为 (1 1 10 6)
 * "KJF" ，将消息分组为 (11 10 6)
 * 注意，消息不能分组为  (1 11 06) ，因为 "06" 不能映射为 "F" ，这是由于 "6" 和 "06" 在映射中并不等价。
 *
 * 给你一个只含数字的 非空 字符串 s ，请计算并返回 解码 方法的 总数 。
 *
 * 题目数据保证答案肯定是一个 32 位 的整数。
 *
 *
 *
 * 示例 1：
 *
 * 输入：s = "12"
 * 输出：2
 * 解释：它可以解码为 "AB"（1 2）或者 "L"（12）。
 * 示例 2：
 *
 * 输入：s = "226"
 * 输出：3
 * 解释：它可以解码为 "BZ" (2 26), "VF" (22 6), 或者 "BBF" (2 2 6) 。
 * 示例 3：
 *
 * 输入：s = "0"
 * 输出：0
 * 解释：没有字符映射到以 0 开头的数字。
 * 含有 0 的有效映射是 'J' -> "10" 和 'T'-> "20" 。
 * 由于没有字符，因此没有有效的方法对此进行解码，因为所有数字都需要映射。
 * 示例 4：
 *
 * 输入：s = "06"
 * 输出：0
 * 解释："06" 不能映射到 "F" ，因为字符串含有前导 0（"6" 和 "06" 在映射中并不等价）。
 *
 *
 * 提示：
 *
 * 1 <= s.length <= 100
 * s 只包含数字，并且可能包含前导零。
 *
 * 来源：力扣（LeetCode）
 * 链接：https://leetcode-cn.com/problems/decode-ways
 * 著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
 *
 * @Author hweiyu
 * @Description
 * @Date 2021/12/14 16:46
 */
public class P0091 {

    public static void main(String[] args) {
        System.out.println(new Solution91().numDecodings("10011"));

        System.out.println(new Solution91().numDecodings("207"));

    }
}

class Solution91 {

    /**
     * 思路：动态规划
     *
     * 对于给定的字符串 s，设它的长度为 n，其中的字符从左到右依次为 s[1], s[2],⋯,s[n]。我们可以使用动态规划的方法计算出字符串 s 的解码方法数。
     *
     * 具体地，设 f(i) 表示字符串 s 的前 i 个字符 s[1..i] 的解码方法数。在进行状态转移时，我们可以考虑最后一次解码使用了 s 中的哪些字符，那么会有下面的两种情况：
     *
     * 第一种情况是我们使用了一个字符，即 s[i] 进行解码，那么只要 s[i] !=0，它就可以被解码成 A∼I 中的某个字母。由于剩余的前 i-1 个字符的解码方法数为 f(i-1)，因此我们可以写出状态转移方程：
     *
     *       f(i) = f(i-1)，其中 s[i] != 0
     *
     * 第二种情况是我们使用了两个字符，即 s[i-1] 和 s[i] 进行编码。与第一种情况类似，s[i-1] 不能等于 0，并且 s[i-1] 和 s[i] 组成的整数必须小于等于 26，这样它们就可以被解码成 J∼Z 中的某个字母。由于剩余的前 i-2 个字符的解码方法数为 f(i-2)，因此我们可以写出状态转移方程：
     *
     *       f(i) = f(i-2)，其中 s[i-1] != 0 并且 10 * s[i-1] + s[i] <= 26
     *
     * 需要注意的是，只有当 i>1 时才能进行转移，否则 s[i-1] 不存在。
     *
     * 将上面的两种状态转移方程在对应的条件满足时进行累加，即可得到 f(i)的值。在动态规划完成后，最终的答案即为 f(n)。
     *
     * 细节
     *
     * 动态规划的边界条件为：f(0) = 1 ，即空字符串可以有 1 种解码方法，解码出一个空字符串。
     *
     * 作者：LeetCode-Solution
     * 链接：https://leetcode-cn.com/problems/decode-ways/solution/jie-ma-fang-fa-by-leetcode-solution-p8np/
     * 来源：力扣（LeetCode）
     * 著作权归作者所有。商业转载请联系作者获得授权，非商业转载请注明出处。
     *
     * @param s
     * @return
     */
    public int numDecodings(String s) {
        if ('0' == s.charAt(0)) {
            return 0;
        }
        if (s.length() == 1) {
            return 1;
        }
        for (int i = 1; i < s.length(); i++) {
            if (s.charAt(i) == '0' && (s.charAt(i - 1) > '2' || s.charAt(i - 1) == '0')) {
                return 0;
            }
        }
        int[] dp = new int[s.length() + 1];
        dp[0] = 1;
        for (int i = 1; i <= s.length(); i++) {
            if (s.charAt(i - 1) != '0') {
                dp[i] += dp[i - 1];
            }
            if (i > 1 && s.charAt(i - 2) != '0' && Integer.valueOf(s.substring(i - 2, i)) <= 26) {
                dp[i] += dp[i - 2];
            }
        }
        return dp[s.length()];
    }
}
